All files / src/app/recipes/[id]/components RecipeIngredients.tsx

0% Statements 0/11
0% Branches 0/4
0% Functions 0/4
0% Lines 0/11

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117                                                                                                                                                                                                                                         
import {
  ActionIcon,
  Box,
  Checkbox,
  Group,
  NumberInput,
  Paper,
  Stack,
  Text,
  Title,
} from '@mantine/core';
import { IconMinus, IconPlus } from '@tabler/icons-react';
import { useTranslations } from 'next-intl';
import classes from '../RecipeDetail.module.css';
import type { RecipeIngredientsProps } from '../types';
import { scaleQuantity } from '../utils';
 
const SERVING_MIN = 1;
const SERVING_MAX = 20;
 
export const RecipeIngredients = ({
  ingredients,
  servingMultiplier,
  adjustedServings,
  checkedIngredients,
  onToggleIngredient,
  onIncrementServings,
  onDecrementServings,
}: Readonly<RecipeIngredientsProps>) => {
  const translate = useTranslations('recipeDetail');
  const ingTranslate = useTranslations('recipeIngredients');
 
  return (
    <Paper p="lg" radius="md" withBorder className={classes.ingredientsCard}>
      <Group justify="space-between" mb="md">
        <Title order={2} size="h3" c="pink">
          {translate('ingredients')}
        </Title>
        <Text size="xs" c="dimmed">
          {translate('checkedOff', {
            count: checkedIngredients.size,
            total: ingredients.length,
          })}
        </Text>
      </Group>
 
      {/* Serving adjuster */}
      <Group gap="xs" mb="lg" justify="center">
        <Text fw={700} size="sm" tt="uppercase">
          {translate('servings')}:
        </Text>
        <ActionIcon
          variant="filled"
          color="pink"
          size="sm"
          onClick={onDecrementServings}
          disabled={servingMultiplier <= SERVING_MIN}
          aria-label={ingTranslate('decreaseServings')}
        >
          <IconMinus size={14} />
        </ActionIcon>
        <NumberInput
          value={adjustedServings}
          readOnly
          hideControls
          w={50}
          size="xs"
          styles={{ input: { textAlign: 'center', fontWeight: 700 } }}
        />
        <ActionIcon
          variant="filled"
          color="pink"
          size="sm"
          onClick={onIncrementServings}
          disabled={servingMultiplier >= SERVING_MAX}
          aria-label={ingTranslate('increaseServings')}
        >
          <IconPlus size={14} />
        </ActionIcon>
      </Group>
 
      {/* Ingredient list */}
      <Stack gap={0}>
        {ingredients.map((ing) => {
          const checked = checkedIngredients.has(ing.localId);
          const scaledQty = scaleQuantity(ing.quantity, servingMultiplier);
          return (
            <Box
              key={ing.localId}
              className={`${classes.ingredientItem} ${checked ? classes.ingredientChecked : ''}`}
              onClick={() => onToggleIngredient(ing.localId)}
            >
              <Checkbox
                checked={checked}
                onChange={() => onToggleIngredient(ing.localId)}
                color="pink"
                size="sm"
                tabIndex={-1}
                aria-label={ing.name}
              />
              <Text
                size="sm"
                className={`${classes.ingredientText} ${checked ? classes.ingredientTextChecked : ''}`}
              >
                <Text component="span" fw={700}>
                  {scaledQty} {ing.unit}
                </Text>{' '}
                {ing.name}
              </Text>
            </Box>
          );
        })}
      </Stack>
    </Paper>
  );
};